package org.bjf.config.ds;

import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.shardingsphere.driver.api.ShardingSphereDataSourceFactory;
import org.apache.shardingsphere.infra.config.algorithm.AlgorithmConfiguration;
import org.apache.shardingsphere.readwritesplitting.api.ReadwriteSplittingRuleConfiguration;
import org.apache.shardingsphere.readwritesplitting.api.rule.ReadwriteSplittingDataSourceRuleConfiguration;
import org.apache.shardingsphere.readwritesplitting.api.strategy.StaticReadwriteSplittingStrategyConfiguration;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

//@Configuration
//@MapperScan(basePackages = ShardingDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "shardSessionFactory")
public class ShardingDataSourceConfig {

    static final String PACKAGE = "org.bjf.modules.shardDemo.mapper";
    private static final String MAPPERLOCATIONS = "classpath*:mapper/shardDemo/**/*.xml";
    private static final String TYPEALIASESPACKAGE = "org.bjf.modules.shardDemo.bean";


    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "custom.datasource.sharding.master")
    public DataSource dataSource() {
        return new DruidDataSource();
    }

    @Bean(name = "slaveDataSource")
    @ConfigurationProperties(prefix = "custom.datasource.sharding.slave")
    public DataSource slaveDataSource() {
        return new DruidDataSource();
    }

    @Bean(name = "shardDataSource")
    public DataSource shardDataSource(@Qualifier("masterDataSource") DataSource master,
                                      @Qualifier("slaveDataSource") DataSource slave)
            throws SQLException {
        //===1.构建shard数据源
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        dataSourceMap.put("master", master);
        dataSourceMap.put("slave0", slave);

        //===2.构建读写分离配置
        //主从数据源配置
        List<ReadwriteSplittingDataSourceRuleConfiguration> dsConfig = Lists.newArrayList();
        dsConfig.add(new ReadwriteSplittingDataSourceRuleConfiguration("ds",
                new StaticReadwriteSplittingStrategyConfiguration("master_ds", Lists.newArrayList("slave0")),
                null, "load_balancer"));

        //负载均衡算法配置
        Map<String, AlgorithmConfiguration> loadBalanceMap = Maps.newHashMap();
        loadBalanceMap.put("load_balancer", new AlgorithmConfiguration("ROUND_ROBIN", new Properties()));
        ReadwriteSplittingRuleConfiguration ruleConfig = new ReadwriteSplittingRuleConfiguration(dsConfig, loadBalanceMap);

        //===3.返回shard数据源
        return ShardingSphereDataSourceFactory.createDataSource(dataSourceMap, Lists.newArrayList(ruleConfig), new Properties());
    }

    @Bean(name = "shardTransactionManager")
    public DataSourceTransactionManager transactionManager(
            @Qualifier("shardDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "shardSessionFactory")
    public SqlSessionFactory sessionFactory(
            @Qualifier("shardDataSource") DataSource dataSource) throws Exception {

        //===1.mybatis-plus 配置
        DbConfig dbCfg = new DbConfig();
        //主键策略
        dbCfg.setIdType(IdType.AUTO);
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setDbConfig(dbCfg);

        // 字段的驼峰下划线转换
        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setMapUnderscoreToCamelCase(Boolean.TRUE);
        //===2.构造sessionFactory(mybatis-plus)
        final MybatisSqlSessionFactoryBean sf = new MybatisSqlSessionFactoryBean();
        sf.setDataSource(dataSource);
        sf.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPERLOCATIONS));
        sf.setTypeAliasesPackage(TYPEALIASESPACKAGE);
        sf.setGlobalConfig(globalConfig);
        // 分页插件
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        sf.setPlugins(mybatisPlusInterceptor);

        return sf.getObject();
    }
}